package issue

import (
	"errors"
	"fmt"
	"github.com/avast/retry-go"
	"math/rand"
	"path"
	"strings"
	"time"
	"wf-deploy-agent/cmd/issue/base"
	"wf-deploy-agent/conf"
	"wf-deploy-agent/infra"
	"wf-deploy-agent/notify"
	"wf-deploy-agent/plugin"
	"wf-deploy-agent/registry"
	"wf-deploy-agent/remote/entity"
	"wf-deploy-agent/util"
)

// Entrance 文件分发入口
type Entrance struct {
}

// Download 下载文件
func (entrance *Entrance) Download(command *base.IssueCommand, item *base.IssueItem) error {
	originUrl := command.GetOriginUrl(item)
	infra.Logger.Debugf("origin url is %s", originUrl)
	loader := &plugin.Loader{
		BasePlugin: &plugin.BasePlugin{Name: "loader"},
	}
	err := loader.Download(originUrl, command.GetOutputDir(item), item.FileName, command.SpeedLimit)
	if err != nil {
		err = errors.New(fmt.Sprintf("call loader plugin occurs err, %v", err))
		return err
	}

	return nil
}

// CheckMD5 校验本地文件MD5是否和原始MD5相等
func (entrance *Entrance) CheckMD5(command *base.IssueCommand, item *base.IssueItem) error {
	filepath := item.GetFilePath(command)
	md5 := util.GetFileMD5(filepath)
	if strings.ToUpper(md5) != strings.ToUpper(item.MD5) {
		err := errors.New(fmt.Sprintf("local file 【%s】 md5 is %s, but original md5 is %s", filepath, md5, item.MD5))
		return err
	}

	return nil
}

// Copy 复制文件
func (entrance *Entrance) Copy(command *base.IssueCommand, item *base.IssueItem) error {
	sourceFile := item.GetFilePath(command)
	targetDir := path.Join(command.TargetPath, item.Version)
	err := util.MkdirNX(targetDir)
	if err != nil {
		return err
	}

	targetFile := path.Join(targetDir, item.FileName)
	return util.CopyFile(targetFile, sourceFile)
}

func (entrance *Entrance) notify(journal *entity.IssueItemJournal) error {
	return retry.Do(func() error {
		return notify.Broadcast(conf.CF.Kafka.IssueItemTopic, journal)
	})
}

// Exec 执行命令
func (entrance *Entrance) Exec(command *base.IssueCommand) error {
	if command.TargetPath == "" {
		err := errors.New("target location is empty")
		infra.Logger.Errorln(err)
		return err
	}

	err := util.ClearDir(command.TargetPath)
	if err != nil {
		err := errors.New(fmt.Sprintf("clear dir[%s] occurs err, %v", command.TargetPath, err))
		infra.Logger.Errorln(err)
		return err
	}

	err = util.MkdirNX(command.TargetPath)
	if err != nil {
		err := errors.New(fmt.Sprintf("create dir[%s] occurs err, %v", command.TargetPath, err))
		infra.Logger.Errorln(err)
		return err
	}

	//随机阻塞10秒内，降低源服务器压力
	<-time.After(time.Second * time.Duration(rand.Intn(10)))

	errs := make([]error, 0)
	for _, item := range command.Items {
		journal := &entity.IssueItemJournal{
			OrderId:    command.OrderId,
			BatchNO:    command.BatchNO,
			ResourceId: conf.CF.Agent.Id,
			ItemId:     item.ItemId,
			State:      1,
			Result:     "",
		}

		_ = entrance.notify(journal)

		err := retry.Do(
			func() error {
				//调用插件
				err = entrance.Download(command, item)
				if err != nil {
					infra.Logger.Errorln(err)
					return err
				}

				//校验本地文件MD5是否和原始MD5相等
				err = entrance.CheckMD5(command, item)
				if err != nil {
					infra.Logger.Errorln(err)
					return err
				}

				//从临时目录拷贝到目标目录
				err = entrance.Copy(command, item)
				if err != nil {
					infra.Logger.Errorln(err)
					return err
				}

				return nil
			},
		)

		if err != nil {
			journal.State = 2
			journal.Result = fmt.Sprint(err)
			errs = append(errs, err)
		} else {
			journal.State = 3
		}

		journal.FinishTime = time.Now().UnixMilli()
		_ = entrance.notify(journal)

		<-time.After(1 * time.Second)
	}

	if len(errs) == 0 {
		//注册仓储实例
		if conf.CF.Agent.Repository.Enable {
			registry.GetRepository().Register(command.BatchNO)
		}

		return nil
	}

	return errors.New(fmt.Sprint(errs))
}
